// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2022 Ivan Baidakou

#if defined(__ANDROID__)
#undef __ANDROID__
#endif

#include "catch.hpp"

#include "rotor-light/queue.hpp"

using namespace rotor_light;

struct StringMessage : Message {
  static constexpr MessageTypeId type_id = __LINE__;
  StringMessage(ActorId to, const char *msg_) : Message(to), string{msg_} {}
  const char *string;
};

using MessageStorage = traits::MessageStorage<StringMessage>;

void reset(ItemGuard &guard) { guard.item_queue->release(); }

TEST_CASE("1 priority, 1 message queue", "[queue]") {
  using AppQueue = Queue<MessageStorage, 1>;
  AppQueue queue;
  printf("queue sz = %lu, message sz = %lu\n", sizeof(queue),
         MessageStorage::item_size);

#ifdef ROTOR_LIGHT_QUEUE_SZ
  CHECK(queue.max_items(0) == 0);
#endif

  CHECK(queue.put<StringMessage, false>(0, 1, "hello"));
#ifdef ROTOR_LIGHT_QUEUE_SZ
  CHECK(queue.max_items(0) == 1);
#endif

  CHECK(!queue.put<StringMessage, false>(0, 1, "world"));

  auto msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("hello"));
  reset(msg);

  CHECK(queue.put<StringMessage, false>(0, 1, "world"));
  CHECK(!queue.put<StringMessage, false>(0, 1, "hello"));
  msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("world"));

  reset(msg);
  CHECK(!queue.next());
#ifdef ROTOR_LIGHT_QUEUE_SZ
  CHECK(queue.max_items(0) == 1);
#endif
}

TEST_CASE("1 priority, 2 messages queue", "[queue]") {
  using AppQueue = Queue<MessageStorage, 2>;
  AppQueue queue;
  printf("queue sz = %lu, message sz = %lu\n", sizeof(queue),
         MessageStorage::item_size);

  CHECK(queue.put<StringMessage, false>(0, 1, "hello"));
  CHECK(queue.put<StringMessage, false>(0, 1, "world"));
  CHECK(!queue.put<StringMessage, false>(0, 1, "!"));

  auto msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("hello"));
  reset(msg);

  msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("world"));
  reset(msg);

  CHECK(queue.put<StringMessage, false>(0, 1, "world"));
  CHECK(queue.put<StringMessage, false>(0, 1, "hello"));
  CHECK(!queue.put<StringMessage, false>(0, 1, "!"));

  msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("world"));
  reset(msg);

  msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("hello"));
  reset(msg);

  CHECK(!queue.next());
#ifdef ROTOR_LIGHT_QUEUE_SZ
  CHECK(queue.max_items(0) == 2);
#endif
}

TEST_CASE("1 priority, 3 messages queue, force put", "[queue]") {
  using AppQueue = Queue<MessageStorage, 3>;
  AppQueue queue;
  CHECK(!queue.put<StringMessage, true>(0, 1, "hello"));
  CHECK(!queue.put<StringMessage, true>(0, 1, "my"));
  CHECK(!queue.put<StringMessage, true>(0, 1, "world"));
  CHECK(queue.put<StringMessage, true>(0, 1, "!"));

  auto msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("my"));
  reset(msg);

  msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("world"));
  reset(msg);

  msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("!"));
  reset(msg);
  CHECK(!queue.next());
#ifdef ROTOR_LIGHT_QUEUE_SZ
  CHECK(queue.max_items(0) == 3);
#endif
}

TEST_CASE("1 priority, 3 messages queue", "[queue]") {
  using AppQueue = Queue<MessageStorage, 3>;
  AppQueue queue;

  CHECK(queue.put<StringMessage, false>(0, 1, "hello"));
  auto msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("hello"));

  CHECK(queue.put<StringMessage, false>(0, 1, "world"));
  reset(msg);
  msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("world"));
}

TEST_CASE("2 priorities, 2/1 messages", "[queue]") {
  using AppQueue = Queue<MessageStorage, 2, 1>;
  AppQueue queue;
  printf("queue sz = %lu, message sz = %lu\n", sizeof(queue),
         MessageStorage::item_size);

  SECTION("order-1") {
    CHECK(queue.put<StringMessage, false>(0, 1, "lorem"));
    CHECK(queue.put<StringMessage, false>(0, 1, "ipsum"));
    CHECK(queue.put<StringMessage, false>(1, 1, "dolor"));
  }

  SECTION("order-2") {
    CHECK(queue.put<StringMessage, false>(1, 1, "dolor"));
    CHECK(queue.put<StringMessage, false>(0, 1, "lorem"));
    CHECK(queue.put<StringMessage, false>(0, 1, "ipsum"));
  }

  SECTION("order-3") {
    CHECK(queue.put<StringMessage, false>(0, 1, "lorem"));
    CHECK(queue.put<StringMessage, false>(1, 1, "dolor"));
    CHECK(queue.put<StringMessage, false>(0, 1, "ipsum"));
  }

  auto msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("dolor"));

  reset(msg);
  msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("lorem"));

  reset(msg);
  msg = queue.next();
  CHECK(static_cast<StringMessage &>(*msg).string == std::string("ipsum"));

  reset(msg);
  CHECK(!queue.next());
}
